package drds.plus.sql_process.utils;

import drds.plus.common.jdbc.Parameters;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.abstract_syntax_tree.configuration.IndexMapping;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.ExecutePlan;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.QueryWithIndex;
import drds.plus.sql_process.abstract_syntax_tree.expression.NullValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.bind_value.BindValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.bind_value.BindValueImpl;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.*;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;
import drds.plus.sql_process.abstract_syntax_tree.node.query.*;
import drds.plus.sql_process.parser.visitor.ExpressionVisitor;
import drds.plus.sql_process.type.Type;
import drds.plus.sql_process.type.Types;
import drds.tools.ShouldNeverHappenException;

import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;


public class OptimizerUtils {

    public static Object convertType(Object value, Type type) {
        if (value == null) {
            return null;
        }

        if (type == null || value instanceof BindValueImpl || value instanceof Function || value instanceof NullValue) {
            return value;
        }

        if (Types.isDateType(type)) {
            // 针对时间类型，不做转换
            // 针对where date < '2014-05-23 00:00:01'，如果按照date类型进行转换，会丢失秒精度，导致结果出错
            return value;
        } else {
            return type.convert(value);
        }
    }

    public static Filter copyFilter(Filter filter) {
        return (Filter) (filter == null ? null : filter.copy());
    }

    public static Set<Item> copySelectItemList(Set<Item> itemSet) {
        if (itemSet == null) {
            return null;
        }
        Set<Item> itemSet1 = new HashSet(itemSet.size());
        for (Item c : itemSet) {
            itemSet1.add(c.copy());
        }

        return itemSet1;
    }

    public static List<Item> copySelectItemList(List<Item> itemList) {
        if (itemList == null) {
            return null;
        }

        List<Item> itemList1 = new ArrayList(itemList.size());
        for (Item item : itemList) {
            itemList1.add(item.copy());
        }

        return itemList1;
    }

    public static Object copyValue(Object obj) {
        if (obj instanceof BindValue) {
            return ((BindValue) obj).copy();
        } else if (obj instanceof Item) {
            return ((Item) obj).copy();
        } else if (obj instanceof OrderBy) {
            return ((OrderBy) obj).copy();
        } else {
            return obj;
        }
    }

    public static List<Object> copyValues(List<Object> objectList) {
        if (objectList == null) {
            return null;
        }

        List<Object> objectList1 = new ArrayList<Object>(objectList.size());
        for (Object obj : objectList) {
            objectList1.add(OptimizerUtils.copyValue(obj));
        }

        return objectList1;
    }

    public static List copyFilter(List filterList) {
        if (filterList == null) {
            return null;
        }
        List newFilters = new ArrayList(filterList.size());
        for (Object obj : filterList) {
            Filter filter = (Filter) obj;
            newFilters.add(filter.copy());
        }
        return newFilters;
    }

    public static List<OrderBy> copyOrderByList(List<OrderBy> orderByList) {
        if (orderByList == null) {
            return null;
        }

        List<OrderBy> orderByList1 = new ArrayList<OrderBy>(orderByList.size());

        for (OrderBy orderBy : orderByList) {
            orderByList1.add(orderBy.copy());
        }

        return orderByList1;
    }

    public static List<Item> copySelectItemList(List<Item> itemList, String oldTableName, String tableName) {
        if (tableName == null) {
            return copySelectItemList(itemList);
        }

        if (itemList == null) {
            return null;
        }

        List<Item> itemList1 = new ArrayList(itemList.size());
        for (Item item : itemList) {
            Item copy = item.copy();
            if (copy instanceof Column) {
                setColumn((Column) copy, oldTableName, tableName);
            } else if (copy instanceof Filter) {
                setFilter((Filter) copy, oldTableName, tableName);
            } else if (copy instanceof Function) {
                setFunction((Function) copy, oldTableName, tableName);
            }

            itemList1.add(copy);
        }

        return itemList1;
    }

    public static List<OrderBy> copyOrderBys(List<OrderBy> orderByList, String oldTableName, String tableName) {
        if (tableName == null) {
            return copyOrderByList(orderByList);
        }

        if (orderByList == null) {
            return null;
        }

        List<OrderBy> orderByList1 = new ArrayList(orderByList.size());
        for (OrderBy orderBy : orderByList) {
            OrderBy copy = orderBy.copy();
            if (copy.getItem() instanceof Column) {
                setColumn((Column) copy.getItem(), oldTableName, tableName);
            } else if (copy.getItem() instanceof Filter) {
                setFilter((Filter) copy.getItem(), oldTableName, tableName);
            } else if (copy.getItem() instanceof Function) {
                setFunction((Function) copy.getItem(), oldTableName, tableName);
            }

            orderByList1.add(copy);
        }

        return orderByList1;
    }

    public static Filter copyFilter(Filter filter, String oldTableName, String tableName) {
        if (filter == null) {
            return null;
        }

        Filter copy = (Filter) filter.copy();
        if (tableName != null) {
            setFilter(copy, oldTableName, tableName);
        }
        return copy;
    }

    private static void setFunction(Function function, String oldTableName, String tableName) {
        for (Object arg : function.getArgList()) {
            if (arg instanceof Item) {
                if (arg instanceof Column) {
                    setColumn((Column) arg, oldTableName, tableName);
                } else if (arg instanceof Filter) {
                    setFilter((Filter) arg, oldTableName, tableName);
                } else if (arg instanceof Function) {
                    setFunction((Function) arg, oldTableName, tableName);
                }
            }

        }
    }

    private static void setFilter(Filter filter, String oldTableName, String tableName) {
        if (filter instanceof BooleanFilter) {
            Object column = ((BooleanFilter) filter).getColumn();
            if (column instanceof Column) {
                setColumn((Column) column, oldTableName, tableName);
            } else if (column instanceof Filter) {
                setFilter((Filter) column, oldTableName, tableName);
            } else if (column instanceof Function) {
                setFunction((Function) column, oldTableName, tableName);
            }

            Object value = ((BooleanFilter) filter).getValue();
            if (value instanceof Column) {
                setColumn((Column) value, oldTableName, tableName);
            } else if (value instanceof Filter) {
                setFilter((Filter) value, oldTableName, tableName);
            } else if (value instanceof Function) {
                setFunction((Function) value, oldTableName, tableName);
            }
        } else if (filter instanceof LogicalOperationFilter) {
            for (Filter sf : ((LogicalOperationFilter) filter).getFilterList()) {
                setFilter(sf, oldTableName, tableName);
            }
        } else if (filter instanceof OrsFilter) {
            for (Filter sf : ((OrsFilter) filter).getFilterList()) {
                setFilter(sf, oldTableName, tableName);
            }
        }
    }

    private static void setColumn(Column column, String oldTableName, String tableName) {
        if (tableName != null && column.getTableName() != null && (oldTableName == null || column.getTableName().equals(oldTableName))) {
            // 针对oldTableName为null时，也允许更新
            column.setTableName(tableName);
        }
    }

    /**
     * 根据索引信息，构建orderby条件
     */
    public static List<OrderBy> getOrderByList(IndexMapping indexMapping) {
        if (indexMapping == null) {
            return new ArrayList<OrderBy>(0);
        }

        List<OrderBy> orderByList = new ArrayList<OrderBy>();
        for (ColumnMetaData columnMetaData : indexMapping.getKeyColumnMetaDataList()) {
            Column column = ObjectCreateFactory.createColumn();
            column.setTableName(columnMetaData.getTableName());
            column.setColumnName(columnMetaData.getColumnName());
            column.setType(columnMetaData.getType());
            column.setAlias(columnMetaData.getAlias());
            column.setAutoIncrement(columnMetaData.isAutoIncrement());
            OrderBy orderBy = ObjectCreateFactory.createOrderBy();
            orderBy.setColumn(column);
            orderBy.setAsc(true);
            orderByList.add(orderBy);
        }
        return orderByList;
    }

    /**
     * 根据column string构造{@linkplain Item}对象
     *
     * @param columnStr
     * @return
     */
    public static Item createColumnFromString(String columnStr) {
        if (columnStr == null) {
            return null;
        }

        // 别名只能单独处理
        if (columnStr.contains(" as ")) {
            String[] strings = columnStr.split(" as ");
            if (strings.length != 2) {
                throw new RuntimeException("createColumnFromString:" + columnStr);
            }
            Item item = createColumnFromString(strings[0].trim());
            item.setAlias(strings[1].trim());
            return item;
        } else {
            ExpressionVisitor expressionVisitor = ExpressionVisitor.parser(columnStr);
            Object columnOrValue = ExpressionVisitor.parser(columnStr).getObject();
            if (columnOrValue instanceof Item) {
                return (Item) columnOrValue;
            } else if (columnOrValue instanceof Filter) {
                return (Filter) columnOrValue;
            } else { // 可能是常量
                return expressionVisitor.buildConstantFilter(columnOrValue);
            }
        }
    }

    public static Column columnMetaDataToColumn(ColumnMetaData columnMetaData, String tableName) {
        Column column = ObjectCreateFactory.createColumn();
        column.setType(columnMetaData.getType());
        column.setColumnName(columnMetaData.getColumnName());
        column.setTableName(tableName);
        column.setAlias(columnMetaData.getAlias());
        column.setAutoIncrement(columnMetaData.isAutoIncrement());
        return column;
    }

    public static Column columnMetaDataToColumn(ColumnMetaData columnMetaData) {
        Column column = ObjectCreateFactory.createColumn();
        column.setType(columnMetaData.getType());
        column.setColumnName(columnMetaData.getColumnName());
        column.setTableName(columnMetaData.getTableName());
        column.setAlias(columnMetaData.getAlias());
        column.setAutoIncrement(columnMetaData.isAutoIncrement());
        return column;
    }

    public static Column getColumn(Object column) {
        if (column instanceof Function) {
            Column column1 = ObjectCreateFactory.createColumn();
            column1.setTableName(((Function) column).getTableName());
            column1.setColumnName(((Function) column).getColumnName());
            column1.setAlias(((Function) column).getAlias());
            column1.setType(((Function) column).getType());
            return column1;
        } else if (!(column instanceof Column)) {
            throw new IllegalArgumentException("column :" + column + " is not a icolumn");
        }

        return (Column) column;
    }

    /**
     * 将columnMeta转化为column列
     */
    public static List<Item> columnMetaListToIColumnList(List<ColumnMetaData> columnMetaDataList, String tableName) {
        List<Item> itemList = new ArrayList(columnMetaDataList.size());
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            itemList.add(columnMetaDataToColumn(columnMetaData, tableName));
        }
        return itemList;
    }

    public static List<Item> columnMetaListToIColumnList(List<ColumnMetaData> columnMetaDataList) {
        List<Item> itemList = new ArrayList(columnMetaDataList.size());
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            itemList.add(columnMetaDataToColumn(columnMetaData));
        }

        return itemList;
    }

    public static Query convertExecutePlanToNode(drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query query) {
        if (query == null) {
            return null;
        }

        if (query instanceof QueryWithIndex) {
            return convertExecutePlanToNode((QueryWithIndex) query);
        } else if (query instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join) {
            return convertExecutePlanToNode((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join) query);
        } else if (query instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.MergeQuery) {
            return convertExecutePlanToNode((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.MergeQuery) query);
        } else {
            throw new ShouldNeverHappenException("不支持的类型:" + query);
        }
    }

    public static Query convertExecutePlanToNode(QueryWithIndex queryWithIndex) {
        Query query = null;
        if (queryWithIndex.getSubQuery() == null) {
            query = new TableQueryWithIndex(queryWithIndex.getIndexName());
        } else {
            query = new $Query$();
            query.addNode(convertExecutePlanToNode(queryWithIndex.getSubQuery()));
        }

        query.setAlias(queryWithIndex.getAlias());
        query.setSelectItemListAndSetNeedBuild(queryWithIndex.getItemList());
        query.setConsistent(queryWithIndex.isConsistent());
        query.setGroupByListAndSetNeedBuild(queryWithIndex.getGroupByList());
        query.setIndexQueryKeyFilterAndSetNeedBuild(queryWithIndex.getKeyFilter());
        query.setResultFilterAndSetNeedBuild(queryWithIndex.getValueFilter());
        query.setLimitFrom(queryWithIndex.getLimitFrom());
        query.setLimitTo(queryWithIndex.getLimitTo());
        query.setLockMode(queryWithIndex.getLockMode());
        query.setOrderByListAndSetNeedBuild(queryWithIndex.getOrderByList());
        query.setSql(queryWithIndex.getSql());
        query.having(queryWithIndex.getHaving());
        query.setSubQueryAndSetNeedBuild(queryWithIndex.isSubQuery());
        query.setExistAggregateExpression(queryWithIndex.isExistAggregate());
        query.setOtherJoinOnFilter(queryWithIndex.getOtherJoinOnFilter());
        query.setSubQueryFunctionFilter(queryWithIndex.getSubQueryFilter());
        query.setDataNodeId(queryWithIndex.getDataNodeId());
        query.setSubQueryFunctionFilter(queryWithIndex.getSubQueryFilter());
        query.setSubQueryId(queryWithIndex.getSubQueryFilterId());
        query.build();
        query = query.deepCopy();
        return query;
    }

    public static Join convertExecutePlanToNode(drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join join) {
        Join joinNode = new Join();
        joinNode.setRightNode(convertExecutePlanToNode(join.getRightNode()));
        joinNode.setLeftNode(convertExecutePlanToNode(join.getLeftNode()));
        joinNode.setJoinStrategy(join.getJoinStrategy());
        if (join.isLeftOuterJoin() && join.isRightOuterJoin()) {
            joinNode.setInnerJoin();
        } else if (join.isLeftOuterJoin()) {
            joinNode.setLeftOuterJoin();
        } else if (join.isRightOuterJoin()) {
            joinNode.setRightOuterJoin();
        } else {
            joinNode.setFullOuterJoin();
        }

        int size = join.getLeftJoinColumnList().size();
        for (int i = 0; i < size; i++) {
            joinNode.addJoinKeys(join.getLeftJoinColumnList().get(i), join.getRightJoinColumnList().get(i));
        }
        joinNode.setOrderByListAndSetNeedBuild(join.getOrderByList());
        joinNode.setLimitFrom(join.getLimitFrom());
        joinNode.setLimitTo(join.getLimitTo());
        joinNode.setConsistent(join.isConsistent());
        joinNode.setResultFilterAndSetNeedBuild(join.getValueFilter());
        joinNode.having(join.getHaving());
        joinNode.setAlias(join.getAlias());
        joinNode.setGroupByListAndSetNeedBuild(join.getGroupByList());
        joinNode.setSubQueryAndSetNeedBuild(join.isSubQuery());
        joinNode.setOtherJoinOnFilter(join.getOtherJoinOnFilter());
        joinNode.setSelectItemListAndSetNeedBuild((join.getItemList()));
        joinNode.setAllWhereFilter(join.getWhereFilter());
        joinNode.setExistAggregateExpression(join.isExistAggregate());
        joinNode.setDataNodeId(join.getDataNodeId());
        joinNode.setSubQueryId(join.getSubQueryFilterId());
        joinNode.setSubQueryFunctionFilter(join.getSubQueryFilter());
        joinNode.build();
        joinNode = joinNode.deepCopy();
        return joinNode;
    }

    public static MergeQuery convertExecutePlanToNode(drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.MergeQuery mergeQuery) {
        MergeQuery mergeQueryNode = new MergeQuery();
        for (ExecutePlan executePlan : mergeQuery.getExecutePlanList()) {
            if (!(executePlan instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query)) {
                throw new UnsupportedOperationException("不支持将非merge(setWhereAndSetNeedBuild)转为语法树");
            }
            mergeQueryNode.addNode(convertExecutePlanToNode((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query) executePlan));
        }
        mergeQueryNode.setLimitFrom(mergeQuery.getLimitFrom());
        mergeQueryNode.setLimitTo(mergeQuery.getLimitTo());
        mergeQueryNode.setSelectItemListAndSetNeedBuild(mergeQuery.getItemList());
        mergeQueryNode.setAlias(mergeQuery.getAlias());
        mergeQueryNode.setSubQueryAndSetNeedBuild(mergeQuery.isSubQuery());
        mergeQueryNode.setUnion(mergeQuery.isUnion());
        mergeQueryNode.setOrderByListAndSetNeedBuild(mergeQuery.getOrderByList());
        mergeQueryNode.setLimitFrom(mergeQuery.getLimitFrom());
        mergeQueryNode.setLimitTo(mergeQuery.getLimitTo());
        mergeQueryNode.setGroupByListAndSetNeedBuild(mergeQuery.getGroupByList());
        mergeQueryNode.setSharded(mergeQuery.isSharded());
        mergeQueryNode.having(mergeQuery.getHaving());
        mergeQueryNode.setOtherJoinOnFilter(mergeQuery.getOtherJoinOnFilter());
        mergeQueryNode.setExistAggregateExpression(mergeQuery.isExistAggregate());
        mergeQueryNode.setGroupByShardColumns(mergeQuery.isGroupByShardColumns());
        mergeQueryNode.setDistinctShardColumns(mergeQuery.isDistinctGroupByShardColumns());
        mergeQueryNode.setDataNodeId(mergeQuery.getDataNodeId());
        mergeQueryNode.setSubQueryId(mergeQuery.getSubQueryFilterId());
        mergeQueryNode.setSubQueryFunctionFilter(mergeQuery.getSubQueryFilter());
        mergeQueryNode.build();
        mergeQueryNode = mergeQueryNode.deepCopy();
        return mergeQueryNode;
    }

    // --------------------------- assignment --------------------------

    public static Filter assignment(Filter filter, Parameters parameters) {
        if (filter == null) {
            return null;
        }

        return (Filter) filter.assignment(parameters);
    }

    public static Item assignment(Item item, Parameters parameters) {
        if (item == null) {
            return item;
        }

        return item.assignment(parameters);
    }

    public static List<Item> assignment(List<Item> itemList, Parameters parameters) {
        if (itemList == null) {
            return null;
        }
        for (Item item : itemList) {
            assignment(item, parameters);
        }

        return itemList;
    }

    /**
     * 整个执行计划是否无条件
     */
    public static boolean isNoFilter(ExecutePlan executePlan) {
        if (!(executePlan instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query)) {
            return true;
        }

        if (executePlan instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.MergeQuery) {
            for (ExecutePlan executePlan1 : ((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.MergeQuery) executePlan).getExecutePlanList()) {
                return isNoFilter(executePlan1);
            }
        }

        if (executePlan instanceof drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join) {
            return isNoFilter(((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join) executePlan).getLeftNode()) && isNoFilter(((drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Join) executePlan).getRightNode());
        }

        if (executePlan instanceof QueryWithIndex) {
            if (((QueryWithIndex) executePlan).getSubQuery() != null) {
                return isNoFilter(((QueryWithIndex) executePlan).getSubQuery());
            } else {
                return ((QueryWithIndex) executePlan).getKeyFilter() == null && ((QueryWithIndex) executePlan).getValueFilter() == null;
            }

        }

        return true;
    }

    public static Date parseDate(String str, String[] parsePatterns) throws ParseException {
        try {
            return parseDate(str, parsePatterns, Locale.ENGLISH);
        } catch (ParseException e) {
            return parseDate(str, parsePatterns, Locale.getDefault());
        }
    }

    public static Date parseDate(String str, String[] parsePatterns, Locale locale) throws ParseException {
        if ((str == null) || (parsePatterns == null)) {
            throw new IllegalArgumentException("Date and Patterns must not be null");
        }

        SimpleDateFormat simpleDateFormat = null;
        ParsePosition parsePosition = new ParsePosition(0);

        for (int i = 0; i < parsePatterns.length; i++) {
            if (i == 0) {
                simpleDateFormat = new SimpleDateFormat(parsePatterns[0], locale);
            } else {
                simpleDateFormat.applyPattern(parsePatterns[i]);
            }
            parsePosition.setIndex(0);
            Date date = simpleDateFormat.parse(str, parsePosition);
            if ((date != null) && (parsePosition.getIndex() == str.length())) {
                return date;
            }
        }

        throw new ParseException("Unable to parser the date: " + str, -1);
    }


}
